# -*- coding: utf-8 -*-
from MySpi import *
import thosttraderapi as api
import configparser
import threading
import os
import time

#local config check check
if(os.path.exists("config.local.ini")):
	configfile = "config.local.ini"
else:
	configfile = "config.ini"


configins=configparser.ConfigParser()
configins.read(configfile)
#Addr
FrontAddr=configins['network']['tradefront']
#AuthInfo
APPID=configins['authinfo']['appid']
AUTHCODE=configins['authinfo']['authcode']
#BaseInfo
BROKERID=configins['baseinfo']['brokerid']
USERID=configins['baseinfo']['userid']
PASSWORD=configins['baseinfo']['password']
PRITOPICMODE=int(configins['baseinfo']['pritopicmode'])
PUBTOPICMODE=int(configins['baseinfo']['pubtopicmode'])
#search conf
TIMEOUTSEC=int(configins['searchopt']['timeoutsec'])
#ReqQryOption
INVESTORID=configins['ReqQryOption']['investorid']
HEDGEFLAG=configins['ReqQryOption']['hedgeflag']
TRADINGDAY=configins['ReqQryOption']['tradingday']
#TradeReqDepthMD
EXCHID=configins['TradeReqDepthMD']['exchid']
TRQRYMDINSTRID=configins['TradeReqDepthMD']['instrid']
PRODUCTCLASS=configins['TradeReqDepthMD']['productclass']
#OrderInfo
INSTRID=configins['orderinfo']['instrid']
PRICETYPE=configins['orderinfo']['pricetype']
DIR=configins['orderinfo']['dir']
OFFSET=configins['orderinfo']['offset']
HEDGE=configins['orderinfo']['hedge']
PRICE=float(configins['orderinfo']['price'])
VOLUME=int(configins['orderinfo']['volume'])
AUTOSUSPEND=int(configins['orderinfo']['autosuspend'])
CONTICOND=configins['orderinfo']['conticond']
STOPPRICE=float(configins['orderinfo']['stopprice'])
ORDERMEMO=configins['orderinfo']['ordermemo']


#settleinfo file
settlefilename = "./" + str(INVESTORID) + "-" + str(TRADINGDAY) + ".txt"


#DataTitle
ReqQryClsInsTitle = ["reserve1", "ExchangeID", "InstrumentName", "reserve2", "reserve3", "ProductClass", "DeliveryYear", "DeliveryMonth", "MaxMarketOrderVolume", "MinMarketOrderVolume", "MaxLimitOrderVolume", "MinLimitOrderVolume", "VolumeMultiple", "PriceTick", "CreateDate", "OpenDate", "ExpireDate", "StartDelivDate", "EndDelivDate", "InstLifePhase", "IsTrading", "PositionType", "PositionDateType", "LongMarginRatio", "ShortMarginRatio", "MaxMarginSideAlgorithm", "reserve4", "StrikePrice", "OptionsType", "UnderlyingMultiple", "CombinationType", "InstrumentID", "ExchangeInstID", "ProductID", "UnderlyingInstrID", ]
ReqQryInsTitle = ["reserve1", "ExchangeID", "InstrumentName", "reserve2", "reserve3", "ProductClass", "DeliveryYear", "DeliveryMonth", "MaxMarketOrderVolume", "MinMarketOrderVolume", "MaxLimitOrderVolume", "MinLimitOrderVolume", "VolumeMultiple", "PriceTick", "CreateDate", "OpenDate", "ExpireDate", "StartDelivDate", "EndDelivDate", "InstLifePhase", "IsTrading", "PositionType", "PositionDateType", "LongMarginRatio", "ShortMarginRatio", "MaxMarginSideAlgorithm", "reserve4", "StrikePrice", "OptionsType", "UnderlyingMultiple", "CombinationType", "InstrumentID", "ExchangeInstID", "ProductID", "UnderlyingInstrID", ]
ReqQryInsMRTitle = ["reserve1", "InvestorRange", "BrokerID", "InvestorID", "HedgeFlag", "LongMarginRatioByMoney", "LongMarginRatioByVolume", "ShortMarginRatioByMoney", "ShortMarginRatioByVolume", "IsRelative", "ExchangeID", "InvestUnitID", "InstrumentID", ]
ReqQryInsCRTitle = ["reserve1", "InvestorRange", "BrokerID", "InvestorID", "OpenRatioByMoney", "OpenRatioByVolume", "CloseRatioByMoney", "CloseRatioByVolume", "CloseTodayRatioByMoney", "CloseTodayRatioByVolume", "ExchangeID", "BizType", "InvestUnitID", "InstrumentID", ]
ReqQryTraAccTitle = ["BrokerID", "AccountID", "PreMortgage", "PreCredit", "PreDeposit", "PreBalance", "PreMargin", "InterestBase", "Interest", "Deposit", "Withdraw", "FrozenMargin", "FrozenCash", "FrozenCommission", "CurrMargin", "CashIn", "Commission", "CloseProfit", "PositionProfit", "Balance", "Available", "WithdrawQuota", "Reserve", "TradingDay", "SettlementID", "Credit", "Mortgage", "ExchangeMargin", "DeliveryMargin", "ExchangeDeliveryMargin", "ReserveBalance", "CurrencyID", "PreFundMortgageIn", "PreFundMortgageOut", "FundMortgageIn", "FundMortgageOut", "FundMortgageAvailable", "MortgageableFund", "SpecProductMargin", "SpecProductFrozenMargin", "SpecProductCommission", "SpecProductFrozenCommission", "SpecProductPositionProfit", "SpecProductCloseProfit", "SpecProductPositionProfitByAlg", "SpecProductExchangeMargin", "BizType", "FrozenSwap", "RemainSwap", ]
ReqQryDepMarTitle = ["TradingDay", "reserve1", "ExchangeID", "reserve2", "LastPrice", "PreSettlementPrice", "PreClosePrice", "PreOpenInterest", "OpenPrice", "HighestPrice", "LowestPrice", "Volume", "Turnover", "OpenInterest", "ClosePrice", "SettlementPrice", "UpperLimitPrice", "LowerLimitPrice", "PreDelta", "CurrDelta", "UpdateTime", "UpdateMillisec", "BidPrice1", "BidVolume1", "AskPrice1", "AskVolume1", "BidPrice2", "BidVolume2", "AskPrice2", "AskVolume2", "BidPrice3", "BidVolume3", "AskPrice3", "AskVolume3", "BidPrice4", "BidVolume4", "AskPrice4", "AskVolume4", "BidPrice5", "BidVolume5", "AskPrice5", "AskVolume5", "AveragePrice", "ActionDay", "InstrumentID", "ExchangeInstID", "BandingUpperPrice", "BandingLowerPrice", ]


#decorator
def run_time(func):
	def wrap(argv1):
		start_time = time.time()
		rt = func(argv1)
		end_time = time.time()
		print('used time:', (end_time - start_time) * 1000, 'ms')
		return rt
	return wrap

class CTradeApi(api.CThostFtdcTraderApi):
	def __init__(self):
		self.tspi=''
		self.tapi=''
		self.ctlevent=threading.Event()
		#query return
		self.ReqQryClsInsData=[]
		self.ReqQryInsData=[]
		self.ReqQryInsMRData=[]
		self.ReqQryInsCRData=[]
		self.ReqQrySettInfoData=[]
		self.ReqQryTraAccData=[]
		self.ReqQryDepMarData=[]
		self.ReqQryOrderData=[]
		self.exchangeid=""
		self.ordersysid=""
		self.frontinfo=""

	@run_time
	def start(self):
		self.tapi=api.CThostFtdcTraderApi.CreateFtdcTraderApi()
		self.tspi=CTradeSpi(self)
		self.tapi.RegisterFront(FrontAddr)
		self.tapi.RegisterSpi(self.tspi)
		'''
		int type:
		api.THOST_TERT_RESTART = 0
		api.THOST_TERT_RESUME = 1
		api.THOST_TERT_QUICK = 2
		api.THOST_TERT_NONE = 3 (public topic only)
		'''
		self.tapi.SubscribePrivateTopic(PRITOPICMODE)
		self.tapi.SubscribePublicTopic(PUBTOPICMODE)
		self.tapi.Init()
		self.ctlevent.wait(timeout = TIMEOUTSEC)
		if(self.ctlevent.isSet() == True):
			self.ctlevent.clear()
			return 0
		else:
			print("init failed, please check network env")
			return 233

	@run_time
	def ReqAuth(self):
		authfield = api.CThostFtdcReqAuthenticateField()
		authfield.BrokerID=BROKERID
		authfield.UserID=USERID
		authfield.AppID=APPID
		authfield.AuthCode=AUTHCODE
		self.tapi.ReqAuthenticate(authfield,0)
		PrintSendName("ReqAuthenticate")
		self.ctlevent.wait(timeout = TIMEOUTSEC)
		if(self.ctlevent.isSet() == True):
			self.ctlevent.clear()
			return 0
		else:
			return 233

	@run_time
	def ReqLogin(self):
		loginfield = api.CThostFtdcReqUserLoginField()
		loginfield.BrokerID=BROKERID
		loginfield.UserID=USERID
		loginfield.Password=PASSWORD
		loginfield.UserProductInfo="python dll"
		self.tapi.ReqUserLogin(loginfield,0)
		PrintSendName("ReqUserLogin")
		self.ctlevent.wait(timeout = TIMEOUTSEC)
		if(self.ctlevent.isSet() == True):
			self.ctlevent.clear()
			return 0
		else:
			return 233

	def PrintFrontInfo(self):
		currfrontinfo = api.CThostFtdcFrontInfoField()
		self.tapi.GetFrontInfo(currfrontinfo)
		print(f"FrontAddr:{currfrontinfo.FrontAddr}")
		print(f"QryFreq:{currfrontinfo.QryFreq}")
		print(f"FTDPkgFreq:{currfrontinfo.FTDPkgFreq}")

	def ReqQryDepMD(self):
		self.ReqQryDepMarData.clear()
		self.ReqQryDepMarData.append(ReqQryDepMarTitle)
		qrydepthmd = api.CThostFtdcQryDepthMarketDataField()
		qrydepthmd.ExchangeID = EXCHID
		qrydepthmd.InstrumentID = TRQRYMDINSTRID 
		qrydepthmd.ProductClass = PRODUCTCLASS
		self.tapi.ReqQryDepthMarketData(qrydepthmd, 0)
		PrintSendName("ReqQryDepthMarketData")
		self.ctlevent.wait(timeout = TIMEOUTSEC)
		if(self.ctlevent.isSet() == True):
			self.ctlevent.clear()
			StringPrint(self.ReqQryDepMarData)
			return self.ReqQryDepMarData
		else:
			return 233

	def ReqQryTraAcc(self):
		self.ReqQryTraAccData.clear()
		self.ReqQryTraAccData.append(ReqQryTraAccTitle)
		traaccfield = api.CThostFtdcQryTradingAccountField()
		traaccfield.BrokerID = BROKERID
		traaccfield.AccountID = INVESTORID
		self.tapi.ReqQryTradingAccount(traaccfield, 0)
		PrintSendName("ReqQryTradingAccount")
		self.ctlevent.wait(timeout = TIMEOUTSEC)
		if(self.ctlevent.isSet() == True):
			self.ctlevent.clear()
			StringPrint(self.ReqQryTraAccData)
			return self.ReqQryTraAccData
		else:
			return 233

	def ReqQrySettInfo(self):
		self.ReqQrySettInfoData.clear()
		settinfofield = api.CThostFtdcQrySettlementInfoField()
		settinfofield.BrokerID = BROKERID
		settinfofield.AccountID = INVESTORID
		settinfofield.TradingDay = TRADINGDAY
		self.tapi.ReqQrySettlementInfo(settinfofield, 0)
		PrintSendName("ReqQrySettlementInfo")
		self.ctlevent.wait(timeout = TIMEOUTSEC)
		if(self.ctlevent.isSet() == True):
			self.ctlevent.clear()
			StringPrint(self.ReqQrySettInfoData)
			SettleinfoWrite(self.ReqQrySettInfoData, settlefilename)
			return self.ReqQrySettInfoData
		else:
			return 233
	
	def ReqQryClsIns(self):
		self.ReqQryClsInsData.clear()
		self.ReqQryClsInsData.append(ReqQryClsInsTitle)
		insfield = api.CThostFtdcQryClassifiedInstrumentField()
		insfield.TradingType='0'
		insfield.ClassType='0'
		self.tapi.ReqQryClassifiedInstrument(insfield, 0)
		PrintSendName("ReqQryClassifiedInstrument")
		self.ctlevent.wait(timeout = TIMEOUTSEC)
		if(self.ctlevent.isSet() == True):
			self.ctlevent.clear()
			ListPrint(self.ReqQryClsInsData)
			return self.ReqQryClsInsData
		else:
			return 233

	def ReqQryIns(self):
		self.ReqQryInsData.clear()
		self.ReqQryInsData.append(ReqQryInsTitle)
		insfield = api.CThostFtdcQryInstrumentField()
		self.tapi.ReqQryInstrument(insfield, 0)
		PrintSendName("ReqQryInstrument")
		self.ctlevent.wait(timeout = TIMEOUTSEC)
		if(self.ctlevent.isSet() == True):
			self.ctlevent.clear()
			ListPrint(self.ReqQryInsData)
			return self.ReqQryInsData
		else:
			return 233

	def ReqQryInsMR(self):
		self.ReqQryInsMRData.clear()
		self.ReqQryInsMRData.append(ReqQryInsMRTitle)
		insmrfield = api.CThostFtdcQryInstrumentMarginRateField()
		insmrfield.BrokerID = BROKERID
		insmrfield.InvestorID = INVESTORID
		insmrfield.HedgeFlag = HEDGEFLAG
		self.tapi.ReqQryInstrumentMarginRate(insmrfield, 0)
		PrintSendName("ReqQryInstrumentMarginRate")
		self.ctlevent.wait(timeout = TIMEOUTSEC)
		if(self.ctlevent.isSet() == True):
			self.ctlevent.clear()
			ListPrint(self.ReqQryInsMRData)
			return self.ReqQryInsMRData
		else:
			return 233
			os._exit(233)

	def ReqQryInsCR(self):
		self.ReqQryInsCRData.clear()
		self.ReqQryInsCRData.append(ReqQryInsCRTitle)
		inscrfield = api.CThostFtdcQryInstrumentCommissionRateField()
		inscrfield.BrokerID = BROKERID
		inscrfield.InvestorID = INVESTORID
		self.tapi.ReqQryInstrumentCommissionRate(inscrfield, 0)
		PrintSendName("ReqQryInstrumentCommissionRate")
		self.ctlevent.wait(timeout = TIMEOUTSEC)
		if(self.ctlevent.isSet() == True):
			self.ctlevent.clear()
			ListPrint(self.ReqQryInsCRData)
			return self.ReqQryInsCRData
		else:
			return 233

	def ReqSettleInfoCf(self):
		settlecffield = api.CThostFtdcSettlementInfoConfirmField()
		settlecffield.BrokerID = BROKERID
		settlecffield.InvestorID = USERID
		self.tapi.ReqSettlementInfoConfirm(settlecffield, 0)
		PrintSendName("ReqSettlementInfoConfirm")
		self.ctlevent.wait(timeout = TIMEOUTSEC)
		if(self.ctlevent.isSet() == True):
			self.ctlevent.clear()
			return 0
		else:
			return 233

#trade function
	def ReqOrIn(self):
		Orderfield = api.CThostFtdcInputOrderField()
		Orderfield.BrokerID=BROKERID
		Orderfield.InvestorID=USERID
		Orderfield.InstrumentID=INSTRID
		Orderfield.OrderPriceType=PRICETYPE
		Orderfield.Direction=DIR
		Orderfield.CombOffsetFlag=OFFSET
		Orderfield.CombHedgeFlag=HEDGE
		Orderfield.LimitPrice=PRICE
		Orderfield.VolumeTotalOriginal=VOLUME
		Orderfield.TimeCondition=api.THOST_FTDC_TC_GFD
		Orderfield.VolumeCondition=api.THOST_FTDC_VC_AV
		Orderfield.ForceCloseReason=api.THOST_FTDC_FCC_NotForceClose
		Orderfield.IsAutoSuspend=AUTOSUSPEND
		Orderfield.IsSwapOrder=0
		Orderfield.UserForceClose=0
		Orderfield.ContingentCondition=CONTICOND
		Orderfield.StopPrice=STOPPRICE
		Orderfield.OrderMemo=ORDERMEMO
		self.tapi.ReqOrderInsert(Orderfield,0)
		PrintSendName("ReqOrderInsert")

	def ReqPaOrIn(self):
		Orderfield = api.CThostFtdcParkedOrderField()
		Orderfield.BrokerID=BROKERID
		Orderfield.InvestorID=USERID
		Orderfield.UserID=USERID
		Orderfield.InstrumentID=INSTRID
		Orderfield.OrderPriceType=PRICETYPE
		Orderfield.Direction=DIR
		Orderfield.CombOffsetFlag=OFFSET
		Orderfield.CombHedgeFlag=HEDGE
		Orderfield.LimitPrice=PRICE
		Orderfield.VolumeTotalOriginal=VOLUME
		Orderfield.TimeCondition=api.THOST_FTDC_TC_GFD
		Orderfield.VolumeCondition=api.THOST_FTDC_VC_AV
		Orderfield.ContingentCondition=api.THOST_FTDC_CC_Immediately
		Orderfield.ForceCloseReason=api.THOST_FTDC_FCC_NotForceClose
		Orderfield.IsAutoSuspend=AUTOSUSPEND
		Orderfield.IsSwapOrder=0
		Orderfield.UserForceClose=0
		self.tapi.ReqParkedOrderInsert(Orderfield,0)
		PrintSendName("ReqParkedOrderInsert")


	def ReqOrDel(self):
		pDelAction=api.CThostFtdcInputOrderActionField()
		pDelAction.BrokerID=BROKERID
		pDelAction.InvestorID=USERID
		pDelAction.UserID=USERID
		pDelAction.ExchangeID=self.exchangeid
		pDelAction.ActionFlag=api.THOST_FTDC_AF_Delete
		pDelAction.OrderSysID=self.ordersysid
		self.tapi.ReqOrderAction(pDelAction, 0)
		PrintSendName("ReqOrderAction")
	
	def ReqOrDelsysid(self, ordersysinfo):
		pDelAction=api.CThostFtdcInputOrderActionField()
		pDelAction.BrokerID=BROKERID
		pDelAction.InvestorID=USERID
		pDelAction.UserID=USERID
		pDelAction.InstrumentID=ordersysinfo['ExchangeInstID']
		pDelAction.ExchangeID=ordersysinfo['ExchangeID']
		pDelAction.OrderSysID=ordersysinfo['OrderSysID']
		pDelAction.ActionFlag=api.THOST_FTDC_AF_Delete
		self.tapi.ReqOrderAction(pDelAction, 0)
		PrintSendName("ReqOrderAction")

	def ReqOrDelAll(self):
		dellist = []
		print("search order to delete")
		for currdict in self.ReqQryOrderData:
			#print(currdict['OrderStatus'])
			if(currdict['OrderStatus'] in ['1', '3', 'a']):
				dellist.append(currdict)
		print("order to delete:")
		for ordersysinfo in dellist:
			print(ordersysinfo['OrderSysID'])
			self.ReqOrDelsysid(ordersysinfo)
		print("order delete end!")


	def ReqOrHold(self):
		pDelAction=api.CThostFtdcInputOrderActionField()
		pDelAction.BrokerID=BROKERID
		pDelAction.InvestorID=USERID
		pDelAction.UserID=USERID
		pDelAction.ExchangeID=self.exchangeid
		pDelAction.ActionFlag=api.THOST_FTDC_AF_Modify
		pDelAction.OrderSysID=self.ordersysid
		self.tapi.ReqOrderAction(pDelAction, 0)
		PrintSendName("ReqOrderAction")

	def ReqOrAct(self):
		pDelAction=api.CThostFtdcInputOrderActionField()
		pDelAction.BrokerID=BROKERID
		pDelAction.InvestorID=USERID
		pDelAction.UserID=USERID
		pDelAction.ExchangeID=self.exchangeid
		pDelAction.ActionFlag=api.THOST_FTDC_AF_Modify
		pDelAction.OrderSysID=self.ordersysid
		self.tapi.ReqOrderAction(pDelAction, 0)
		PrintSendName("ReqOrderAction")
	
	def ReqQryOr(self):
		pQryOrder=api.CThostFtdcQryOrderField()
		pQryOrder.BrokerID=BROKERID
		pQryOrder.InvestorID=USERID
		self.tapi.ReqQryOrder(pQryOrder, 0)
		PrintSendName("ReqQryOrder")



#print func name
def PrintSendName(sendname):
	sendmsg = "send " + sendname + " ok"
	print(sendmsg)

def ListPrint(datalist):
	for currlist in datalist:
		DealTooMax(currlist)
		listlen = len(currlist)
		formatlist = tuple(currlist)
		print("|%-25s|" * listlen % formatlist)

def StringPrint(datalist):
	for currlist in datalist:
		print("*" * 50)
		print(currlist)
		print("*" * 50)

def SettleinfoWrite(datalist, filename):
	settlefh = open(filename, mode="w")
	for currlist in datalist:
		print(currlist[1], file=settlefh)
	settlefh.close()